using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace RedBlueBalls
{
    /// <summary>
    /// Historical red blue ball record
    /// </summary>
    public class RedBlueBallRecord
    {
        public const string BALL_PATTERN = @"(\d+)-(\d+)-(\d+),(\d+),(\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+)";
        public static Regex ballRegex = new Regex(BALL_PATTERN);
        public const int RED_BALL = 6;
        public const int SEG_LEN = 4;
        private const int SEG2_LOW = 9;
        private const int SEG3_LOW = 17;
        private const int SEG4_LOW = 26;

        DateTime date;
        long sequence;
        int[] reds = new int[RED_BALL]; // 1~33
        int blue; // 1~16

        /// <summary>
        /// Load red blue ball record from file line
        /// </summary>
        /// <param name="line"></param>
        public RedBlueBallRecord(string line)
        {
            if (ballRegex.IsMatch(line))
            {
                Match ballMatch = ballRegex.Match(line);
                date = new DateTime(Convert.ToInt32(ballRegex.Replace(ballMatch.Value, "$1")),
                    Convert.ToInt32(ballRegex.Replace(ballMatch.Value, "$2")),
                    Convert.ToInt32(ballRegex.Replace(ballMatch.Value, "$3")));
                sequence = Convert.ToInt32(ballRegex.Replace(ballMatch.Value, "$4"));
                for (int i = 0; i < RED_BALL; i++)
                {
                    reds[i] = Convert.ToInt32(ballRegex.Replace(ballMatch.Value, String.Format("${0}", 5 + i)));
                }
                blue = Convert.ToInt32(ballRegex.Replace(ballMatch.Value, "$11"));
            }
            else
            {
                throw new Exception("The data in (" + line + ") is invalid");
            }
        }

        /// <summary>
        /// Perdicted red blue ball record
        /// </summary>
        /// <param name="r1"></param>
        /// <param name="r2"></param>
        /// <param name="r3"></param>
        /// <param name="r4"></param>
        /// <param name="r5"></param>
        /// <param name="r6"></param>
        /// <param name="b"></param>
        public RedBlueBallRecord(int r1, int r2, int r3, int r4, int r5, int r6, int b)
        {
            sequence = 0;
            date = new DateTime();
            reds[0] = r1;
            reds[1] = r2;
            reds[2] = r3;
            reds[3] = r4;
            reds[4] = r5;
            reds[5] = r6;
            blue = b;
        }

        #region Red Ball

        /// <summary>
        /// Get the red balls
        /// </summary>
        /// <returns></returns>
        public int[] GetReds()
        {
            return (int[])(reds.Clone());
        }

        /// <summary>
        /// Does The record contain the red ball?
        /// </summary>
        /// <param name="red"></param>
        /// <returns></returns>
        public bool HasRedBall(int red)
        {
            for (int i = 0; i < RED_BALL; i++)
            {
                if (reds[i] == red)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Check how many same red balls between two records
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CountSameReds(RedBlueBallRecord other)
        {
            int count = 0;
            for (int i = 0, j = 0; i < RED_BALL && j < RED_BALL; )
            {
                if (reds[i] < other.reds[j])
                {
                    i++;
                }
                else if (reds[i] > other.reds[j])
                {
                    j++;
                }
                else
                {
                    count++;
                    i++;
                    j++;
                }
            }

            return count;
        }

        /// <summary>
        /// Get the total sum of red balls
        /// </summary>
        /// <returns></returns>
        public int GetSumRed()
        {
            int sum = 0;
            for (int i = 0; i < RED_BALL; i++)
                sum += reds[i];
            return sum;
        }

        /// <summary>
        /// The same delta in red balls
        /// </summary>
        /// <returns></returns>
        public int GetMaxSameDelta()
        {
            List<int> list = new List<int>();
            for (int i = 0; i < RED_BALL - 1; i++)
            {
                for (int j = i + 1; j < RED_BALL; j++)
                {
                    Util.Add(list, reds[j] - reds[i]);
                }
            }

            int current = 0;
            int count = 0;
            int max = 0;
            foreach (int delta in list)
            {
                if (delta == current)
                    count++;
                else
                {
                    if (max < count)
                        max = count;
                    current = delta;
                    count = 1;
                }
            }

            return max;
        }

        /// <summary>
        /// The number of odd red balls
        /// </summary>
        /// <returns></returns>
        public int GetOdds()
        {
            int count = 0;
            for (int i = 0; i < RED_BALL; i++)
                if (reds[i] % 2 == 1)
                    count++;
            return count;
        }

        /// <summary>
        /// The set of red ball digits
        /// </summary>
        /// <returns></returns>
        public HashSet<int> GetDigits()
        {
            HashSet<int> digits = new HashSet<int>();
            for (int i = 0; i < RED_BALL; i++)
            {
                int digit = reds[i] % 10;
                if (!digits.Contains(digit))
                digits.Add(digit);
            }

            return digits;
        }

        /// <summary>
        /// The number of prime red balls
        /// </summary>
        /// <returns></returns>
        public int GetPrimes()
        {
            int count = 0;
            for (int i = 0; i < RED_BALL; i++)
                if (Util.IsPrime(reds[i]))
                    count++;
            return count;
        }

        /// <summary>
        /// The segments of red balls
        /// </summary>
        /// <returns></returns>
        public int[] GetSegments()
        {
            int[] segments = new int[SEG_LEN];
            for (int i = 0; i < RED_BALL; i++)
            {
                int ball = reds[i];
                if (ball < SEG2_LOW)
                    segments[0]++;
                else if (ball < SEG3_LOW)
                    segments[1]++;
                else if (ball < SEG4_LOW)
                    segments[2]++;
                else
                    segments[3]++;
            }

            return segments;
        }

        #endregion Red Ball


        #region Blue Ball

        /// <summary>
        /// Get the blue ball
        /// </summary>
        /// <returns></returns>
        public int GetBlue()
        {
            return blue;
        }

        #endregion Blue Ball

        #region Other

        /// <summary>
        /// Get the day of the record
        /// </summary>
        /// <returns></returns>
        public int GetDay()
        {
            return date.Day;
        }

        /// <summary>
        /// Get the next date after this record
        /// </summary>
        /// <returns></returns>
        public DateTime GetNextDate()
        {
            // Thursday to Sunday
            if (date.DayOfWeek == DayOfWeek.Thursday)
                return date.AddDays(3);
            else
                return date.AddDays(2);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("Red balls: ");
            for (int i = 0; i < RED_BALL; i++)
                sb.Append(reds[i].ToString("D2")).Append(" ");
            sb.Append("Blue ball: ");
            sb.Append(blue.ToString("D2"));

            return sb.ToString();
        }
        #endregion Other
    }
}
